package jaeger

import (
	"fmt"
)

func drawLinksK8s(traces map[string]interface{}) ([]map[string]interface{}, error) {
	var HostMapTotal = make(map[string]map[string]int)
	var HostMapTotalT = make(map[string]map[string][]string)

	for id, trace := range traces {
		hostMap, err := drawOneTraceLinksK8s(trace)
		if err != nil {
			return nil, fmt.Errorf("draw one trace %v %v", id, err)
		}

		for headHost, childs := range hostMap {
			for childHost, value := range childs {
				if headHost != childHost {
					if _, ok := HostMapTotal[headHost]; !ok {
						HostMapTotal[headHost] = make(map[string]int)
						HostMapTotalT[headHost] = make(map[string][]string)
					}
					HostMapTotal[headHost][childHost] += value
					HostMapTotalT[headHost][childHost] = append(HostMapTotalT[headHost][childHost], id)
				}
			}
		}
	}

	var dag = []map[string]interface{}{}
	for headHost, childs := range HostMapTotal {
		for childHost, value := range childs {
			dag = append(dag, map[string]interface{}{
				"source":   headHost,
				"target":   childHost,
				"value":    value,
				"traceIds": HostMapTotalT[headHost][childHost],
			})
		}
	}

	return dag, nil
}

func drawOneTraceLinksK8s(traceI interface{}) (map[string]map[string]int, error) {
	var (
		ok        bool
		trace     map[string]interface{}
		processes map[string]interface{}
		spans     []interface{}
	)

	if trace, ok = traceI.(map[string]interface{}); !ok {
		return nil, fmt.Errorf("trace is not map[string]interface{}")
	}

	if proceI, ok := trace["processes"]; !ok {
		return nil, fmt.Errorf("trace is not contain processes")
	} else {
		if processes, ok = proceI.(map[string]interface{}); !ok {
			return nil, fmt.Errorf("trace processes not map[string]interface{}")
		}
	}

	if spansI, ok := trace["spans"]; !ok {
		return nil, fmt.Errorf("trace is not contain spans")
	} else {
		if spans, ok = spansI.([]interface{}); !ok {
			return nil, fmt.Errorf("trace processes not []interface{}")
		}
	}

	var opToHost = make(map[string]string, len(processes))
	for p, pv := range processes {
		var host string
		if err := getHostFromProcessk8s(pv, &host); err != nil {
			return nil, err
		}
		opToHost[p] = host
	}

	opLinks, err := getDagLink(spans)
	if err != nil {
		return nil, fmt.Errorf("get op link %v", err)
	}

	var hostLinksMap = make(map[string]map[string]int)
	for parentOp, childs := range opLinks {
		for childOp, linkValue := range childs {
			if parentOp != childOp {
				parentHost := opToHost[parentOp]
				childHost := opToHost[childOp]
				if _, ok = hostLinksMap[parentHost]; !ok {
					hostLinksMap[parentHost] = make(map[string]int)
				}
				hostLinksMap[parentHost][childHost] += linkValue
			}
		}
	}

	return hostLinksMap, nil
}

func getDagLink(spans []interface{}) (map[string]map[string]int, error) {

	var spanToOp = make(map[string]string, len(spans))
	var spanLinks = make(map[string]map[string]int)
	for _, spanI := range spans {
		span, ok := spanI.(map[string]interface{})
		if !ok {
			return nil, fmt.Errorf("span is not map[string]interface")
		}

		spanId := span["spanID"].(string)
		processId := span["processID"].(string)

		spanToOp[spanId] = processId

		parentIdRefer := span["references"].([]interface{})
		for _, p := range parentIdRefer {
			parent := p.(map[string]interface{})["spanID"].(string)
			if _, ok = spanLinks[parent]; !ok {
				spanLinks[parent] = make(map[string]int)
				spanLinks[parent][spanId] = 1
			}
			spanLinks[parent][spanId]++
		}
	}

	var opLinks = make(map[string]map[string]int)
	for parentSpan, childSpan := range spanLinks {
		parentOp := spanToOp[parentSpan]
		if _, ok := opLinks[parentOp]; !ok {
			opLinks[parentOp] = make(map[string]int)
		}
		for child, linkValue := range childSpan {
			childOp := spanToOp[child]
			if _, ok := opLinks[parentOp][childOp]; !ok {
				opLinks[parentOp][childOp] = linkValue
			}
			opLinks[parentOp][childOp] += linkValue
		}
	}

	return opLinks, nil
}

func getHostFromProcessk8s(processes interface{}, hostname *string) error {
	var host string

	p1, ok := processes.(map[string]interface{})
	if !ok {
		return fmt.Errorf("processes is not map[string]interface{}")
	}

	//service = p1["serviceName"].(string)

	p2, ok := p1["tags"].([]interface{})
	if !ok {
		return fmt.Errorf("processes tags is not []interface{}")
	}

	for _, v := range p2 {
		//if v.(map[string]interface{})["key"].(string) == "guid" {
		//	guid = v.(map[string]interface{})["value"].(string)
		//}
		if v.(map[string]interface{})["key"].(string) == "hostname" {
			host = v.(map[string]interface{})["value"].(string)
		}
	}

	if host == "" {
		return fmt.Errorf("can't find tag guid or host or service name")
	}

	*hostname = host
	return nil
}
